From 936181f3542c43da7c70e8d8ed880ddfc07e9d2f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 15 Feb 2019 21:30:36 -0500 Subject: [PATCH] Add a GtkTextAccessible Add an accessible implementation for GtkText. --- gtk/a11y/gtktextaccessible.c | 1030 ++++++++++++++++++++++++++++++++++ gtk/a11y/gtktextaccessible.h | 57 ++ gtk/a11y/meson.build | 2 + gtk/gtktext.c | 15 +- 4 files changed, 1091 insertions(+), 13 deletions(-) create mode 100644 gtk/a11y/gtktextaccessible.c create mode 100644 gtk/a11y/gtktextaccessible.h diff --git a/gtk/a11y/gtktextaccessible.c b/gtk/a11y/gtktextaccessible.c new file mode 100644 index 0000000000..6a0bd453b1 --- /dev/null +++ b/gtk/a11y/gtktextaccessible.c @@ -0,0 +1,1030 @@ +/* GTK+ - accessibility implementations + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#define GDK_COMPILATION +#include "gdk/gdkeventsprivate.h" + +#include +#include +#include +#include "gtkpango.h" +#include "gtktextaccessible.h" +#include "gtktextprivate.h" +#include "gtkcomboboxaccessible.h" +#include "gtkstylecontextprivate.h" +#include "gtkwidgetprivate.h" + +struct _GtkTextAccessiblePrivate +{ + gint cursor_position; + gint selection_bound; +}; + +/* Callbacks */ + +static void insert_text_cb (GtkEditable *editable, + gchar *new_text, + gint new_text_length, + gint *position); +static void delete_text_cb (GtkEditable *editable, + gint start, + gint end); + +static gboolean check_for_selection_change (GtkTextAccessible *entry, + GtkText *gtk_text); + + +static void atk_editable_text_interface_init (AtkEditableTextIface *iface); +static void atk_text_interface_init (AtkTextIface *iface); +static void atk_action_interface_init (AtkActionIface *iface); + + +G_DEFINE_TYPE_WITH_CODE (GtkTextAccessible, gtk_text_accessible, GTK_TYPE_WIDGET_ACCESSIBLE, + G_ADD_PRIVATE (GtkTextAccessible) + G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)) + + +static AtkStateSet * +gtk_text_accessible_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + gboolean value; + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); + if (widget == NULL) + return NULL; + + state_set = ATK_OBJECT_CLASS (gtk_text_accessible_parent_class)->ref_state_set (accessible); + + g_object_get (G_OBJECT (widget), "editable", &value, NULL); + if (value) + atk_state_set_add_state (state_set, ATK_STATE_EDITABLE); + atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE); + + return state_set; +} + +static AtkAttributeSet * +gtk_text_accessible_get_attributes (AtkObject *accessible) +{ + GtkWidget *widget; + AtkAttributeSet *attributes; + AtkAttribute *placeholder_text; + const gchar *text; + + attributes = ATK_OBJECT_CLASS (gtk_text_accessible_parent_class)->get_attributes (accessible); + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); + if (widget == NULL) + return attributes; + + text = gtk_text_get_placeholder_text (GTK_TEXT (widget)); + if (text == NULL) + return attributes; + + placeholder_text = g_malloc (sizeof (AtkAttribute)); + placeholder_text->name = g_strdup ("placeholder-text"); + placeholder_text->value = g_strdup (text); + + attributes = g_slist_append (attributes, placeholder_text); + + return attributes; +} + +static void +gtk_text_accessible_initialize (AtkObject *obj, + gpointer data) +{ + GtkText *entry; + GtkTextAccessible *gtk_text_accessible; + gint start_pos, end_pos; + + ATK_OBJECT_CLASS (gtk_text_accessible_parent_class)->initialize (obj, data); + + gtk_text_accessible = GTK_TEXT_ACCESSIBLE (obj); + + entry = GTK_TEXT (data); + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos); + gtk_text_accessible->priv->cursor_position = end_pos; + gtk_text_accessible->priv->selection_bound = start_pos; + + /* Set up signal callbacks */ + g_signal_connect_after (entry, "insert-text", G_CALLBACK (insert_text_cb), NULL); + g_signal_connect (entry, "delete-text", G_CALLBACK (delete_text_cb), NULL); + + if (gtk_text_get_visibility (entry)) + obj->role = ATK_ROLE_TEXT; + else + obj->role = ATK_ROLE_PASSWORD_TEXT; +} + +static void +gtk_text_accessible_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget; + AtkObject* atk_obj; + GtkText* gtk_text; + GtkTextAccessible* entry; + + widget = GTK_WIDGET (obj); + atk_obj = gtk_widget_get_accessible (widget); + gtk_text = GTK_TEXT (widget); + entry = GTK_TEXT_ACCESSIBLE (atk_obj); + + if (g_strcmp0 (pspec->name, "cursor-position") == 0) + { + if (check_for_selection_change (entry, gtk_text)) + g_signal_emit_by_name (atk_obj, "text-selection-changed"); + /* + * The entry cursor position has moved so generate the signal. + */ + g_signal_emit_by_name (atk_obj, "text-caret-moved", + entry->priv->cursor_position); + } + else if (g_strcmp0 (pspec->name, "selection-bound") == 0) + { + if (check_for_selection_change (entry, gtk_text)) + g_signal_emit_by_name (atk_obj, "text-selection-changed"); + } + else if (g_strcmp0 (pspec->name, "editable") == 0) + { + gboolean value; + + g_object_get (obj, "editable", &value, NULL); + atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, value); + } + else if (g_strcmp0 (pspec->name, "visibility") == 0) + { + gboolean visibility; + AtkRole new_role; + + visibility = gtk_text_get_visibility (gtk_text); + new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT; + atk_object_set_role (atk_obj, new_role); + } + else + GTK_WIDGET_ACCESSIBLE_CLASS (gtk_text_accessible_parent_class)->notify_gtk (obj, pspec); +} + +static gint +gtk_text_accessible_get_index_in_parent (AtkObject *accessible) +{ + /* + * If the parent widget is a combo box then the index is 1 + * otherwise do the normal thing. + */ + if (accessible->accessible_parent) + if (GTK_IS_COMBO_BOX_ACCESSIBLE (accessible->accessible_parent)) + return 1; + + return ATK_OBJECT_CLASS (gtk_text_accessible_parent_class)->get_index_in_parent (accessible); +} + +static void +gtk_text_accessible_class_init (GtkTextAccessibleClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass; + + class->ref_state_set = gtk_text_accessible_ref_state_set; + class->get_index_in_parent = gtk_text_accessible_get_index_in_parent; + class->initialize = gtk_text_accessible_initialize; + class->get_attributes = gtk_text_accessible_get_attributes; + + widget_class->notify_gtk = gtk_text_accessible_notify_gtk; +} + +static void +gtk_text_accessible_init (GtkTextAccessible *entry) +{ + entry->priv = gtk_text_accessible_get_instance_private (entry); + entry->priv->cursor_position = 0; + entry->priv->selection_bound = 0; +} + +static gchar * +gtk_text_accessible_get_text (AtkText *atk_text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + if (widget == NULL) + return NULL; + + return gtk_text_get_display_text (GTK_TEXT (widget), start_pos, end_pos); +} + +static gchar * +gtk_text_accessible_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + return _gtk_pango_get_text_before (gtk_text_get_layout (GTK_TEXT (widget)), + boundary_type, offset, + start_offset, end_offset); +} + +static gchar * +gtk_text_accessible_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + return _gtk_pango_get_text_at (gtk_text_get_layout (GTK_TEXT (widget)), + boundary_type, offset, + start_offset, end_offset); +} + +static gchar * +gtk_text_accessible_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + return _gtk_pango_get_text_after (gtk_text_get_layout (GTK_TEXT (widget)), + boundary_type, offset, + start_offset, end_offset); +} + +static gint +gtk_text_accessible_get_character_count (AtkText *atk_text) +{ + GtkWidget *widget; + gchar *text; + glong char_count; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + if (widget == NULL) + return 0; + + text = gtk_text_get_display_text (GTK_TEXT (widget), 0, -1); + + char_count = 0; + if (text) + { + char_count = g_utf8_strlen (text, -1); + g_free (text); + } + + return char_count; +} + +static gint +gtk_text_accessible_get_caret_offset (AtkText *text) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return 0; + + return gtk_editable_get_position (GTK_EDITABLE (widget)); +} + +static gboolean +gtk_text_accessible_set_caret_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + gtk_editable_set_position (GTK_EDITABLE (widget), offset); + + return TRUE; +} + +static AtkAttributeSet * +add_text_attribute (AtkAttributeSet *attributes, + AtkTextAttribute attr, + gint i) +{ + AtkAttribute *at; + + at = g_new (AtkAttribute, 1); + at->name = g_strdup (atk_text_attribute_get_name (attr)); + at->value = g_strdup (atk_text_attribute_get_value (attr, i)); + + return g_slist_prepend (attributes, at); +} + +static AtkAttributeSet * +gtk_text_accessible_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + AtkAttributeSet *attributes; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + attributes = NULL; + attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, + gtk_widget_get_direction (widget)); + attributes = _gtk_pango_get_run_attributes (attributes, + gtk_text_get_layout (GTK_TEXT (widget)), + offset, + start_offset, + end_offset); + + return attributes; +} + +static AtkAttributeSet * +gtk_text_accessible_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + AtkAttributeSet *attributes; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + attributes = NULL; + attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, + gtk_widget_get_direction (widget)); + attributes = _gtk_pango_get_default_attributes (attributes, + gtk_text_get_layout (GTK_TEXT (widget))); + attributes = _gtk_style_context_get_attributes (attributes, + gtk_widget_get_style_context (widget)); + + return attributes; +} + +static void +gtk_text_accessible_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkText *entry; + PangoRectangle char_rect; + gchar *entry_text; + gint index, x_layout, y_layout; + GdkSurface *surface; + gint x_surface, y_surface; + GtkAllocation allocation; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + entry = GTK_TEXT (widget); + + gtk_text_get_layout_offsets (entry, &x_layout, &y_layout); + entry_text = gtk_text_get_display_text (entry, 0, -1); + index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text; + g_free (entry_text); + + pango_layout_index_to_pos (gtk_text_get_layout (entry), index, &char_rect); + pango_extents_to_pixels (&char_rect, NULL); + + gtk_widget_get_allocation (widget, &allocation); + + surface = gtk_widget_get_surface (widget); + gdk_surface_get_origin (surface, &x_surface, &y_surface); + + *x = x_surface + allocation.x + x_layout + char_rect.x; + *y = y_surface + allocation.y + y_layout + char_rect.y; + *width = char_rect.width; + *height = char_rect.height; + + if (coords == ATK_XY_WINDOW) + { + surface = gdk_surface_get_toplevel (surface); + gdk_surface_get_origin (surface, &x_surface, &y_surface); + + *x -= x_surface; + *y -= y_surface; + } +} + +static gint +gtk_text_accessible_get_offset_at_point (AtkText *atk_text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkText *entry; + gchar *text; + gint index, x_layout, y_layout; + gint x_surface, y_surface; + gint x_local, y_local; + GdkSurface *surface; + glong offset; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + if (widget == NULL) + return -1; + + entry = GTK_TEXT (widget); + + gtk_text_get_layout_offsets (entry, &x_layout, &y_layout); + + surface = gtk_widget_get_surface (widget); + gdk_surface_get_origin (surface, &x_surface, &y_surface); + + x_local = x - x_layout - x_surface; + y_local = y - y_layout - y_surface; + + if (coords == ATK_XY_WINDOW) + { + surface = gdk_surface_get_toplevel (surface); + gdk_surface_get_origin (surface, &x_surface, &y_surface); + + x_local += x_surface; + y_local += y_surface; + } + if (!pango_layout_xy_to_index (gtk_text_get_layout (entry), + x_local * PANGO_SCALE, + y_local * PANGO_SCALE, + &index, NULL)) + { + if (x_local < 0 || y_local < 0) + index = 0; + else + index = -1; + } + + offset = -1; + if (index != -1) + { + text = gtk_text_get_display_text (entry, 0, -1); + offset = g_utf8_pointer_to_offset (text, text + index); + g_free (text); + } + + return offset; +} + +static gint +gtk_text_accessible_get_n_selections (AtkText *text) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return 0; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + return 1; + + return 0; +} + +static gchar * +gtk_text_accessible_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + if (selection_num != 0) + return NULL; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos, end_pos)) + return gtk_editable_get_chars (GTK_EDITABLE (widget), *start_pos, *end_pos); + + return NULL; +} + +static gboolean +gtk_text_accessible_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkText *entry; + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + entry = GTK_TEXT (widget); + + if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gtk_text_accessible_remove_selection (AtkText *text, + gint selection_num) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + if (selection_num != 0) + return FALSE; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (widget), end, end); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gtk_text_accessible_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + if (selection_num != 0) + return FALSE; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (widget), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gunichar +gtk_text_accessible_get_character_at_offset (AtkText *atk_text, + gint offset) +{ + GtkWidget *widget; + gchar *text; + gchar *index; + gunichar result; + + result = '\0'; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + if (widget == NULL) + return result; + + if (!gtk_text_get_visibility (GTK_TEXT (widget))) + return result; + + text = gtk_text_get_display_text (GTK_TEXT (widget), 0, -1); + if (offset < g_utf8_strlen (text, -1)) + { + index = g_utf8_offset_to_pointer (text, offset); + result = g_utf8_get_char (index); + g_free (text); + } + + return result; +} + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + iface->get_text = gtk_text_accessible_get_text; + iface->get_character_at_offset = gtk_text_accessible_get_character_at_offset; + iface->get_text_before_offset = gtk_text_accessible_get_text_before_offset; + iface->get_text_at_offset = gtk_text_accessible_get_text_at_offset; + iface->get_text_after_offset = gtk_text_accessible_get_text_after_offset; + iface->get_caret_offset = gtk_text_accessible_get_caret_offset; + iface->set_caret_offset = gtk_text_accessible_set_caret_offset; + iface->get_character_count = gtk_text_accessible_get_character_count; + iface->get_n_selections = gtk_text_accessible_get_n_selections; + iface->get_selection = gtk_text_accessible_get_selection; + iface->add_selection = gtk_text_accessible_add_selection; + iface->remove_selection = gtk_text_accessible_remove_selection; + iface->set_selection = gtk_text_accessible_set_selection; + iface->get_run_attributes = gtk_text_accessible_get_run_attributes; + iface->get_default_attributes = gtk_text_accessible_get_default_attributes; + iface->get_character_extents = gtk_text_accessible_get_character_extents; + iface->get_offset_at_point = gtk_text_accessible_get_offset_at_point; +} + +static void +gtk_text_accessible_set_text_contents (AtkEditableText *text, + const gchar *string) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + if (!gtk_editable_get_editable (GTK_EDITABLE (widget))) + return; + + gtk_editable_set_text (GTK_EDITABLE (widget), string); +} + +static void +gtk_text_accessible_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position) +{ + GtkWidget *widget; + GtkEditable *editable; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_editable_insert_text (editable, string, length, position); + gtk_editable_set_position (editable, *position); +} + +static void +gtk_text_accessible_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + gchar *str; + GdkClipboard *clipboard; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + str = gtk_editable_get_chars (editable, start_pos, end_pos); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_set_text (clipboard, str); + g_free (str); +} + +static void +gtk_text_accessible_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + gchar *str; + GdkClipboard *clipboard; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + str = gtk_editable_get_chars (editable, start_pos, end_pos); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_set_text (clipboard, str); + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +static void +gtk_text_accessible_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +typedef struct +{ + GtkText* entry; + gint position; +} PasteData; + +static void +paste_received_cb (GObject *clipboard, + GAsyncResult *result, + gpointer data) +{ + PasteData *paste = data; + char *text; + + text = gdk_clipboard_read_text_finish (GDK_CLIPBOARD (clipboard), result, NULL); + if (text) + gtk_editable_insert_text (GTK_EDITABLE (paste->entry), text, -1, + &paste->position); + + g_object_unref (paste->entry); + g_free (paste); + g_free (text); +} + +static void +gtk_text_accessible_paste_text (AtkEditableText *text, + gint position) +{ + GtkWidget *widget; + GtkEditable *editable; + PasteData *paste; + GdkClipboard *clipboard; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + paste = g_new0 (PasteData, 1); + paste->entry = GTK_TEXT (widget); + paste->position = position; + + g_object_ref (paste->entry); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_read_text_async (clipboard, NULL, paste_received_cb, paste); +} + +static void +atk_editable_text_interface_init (AtkEditableTextIface *iface) +{ + iface->set_text_contents = gtk_text_accessible_set_text_contents; + iface->insert_text = gtk_text_accessible_insert_text; + iface->copy_text = gtk_text_accessible_copy_text; + iface->cut_text = gtk_text_accessible_cut_text; + iface->delete_text = gtk_text_accessible_delete_text; + iface->paste_text = gtk_text_accessible_paste_text; + iface->set_run_attributes = NULL; +} + +static void +insert_text_cb (GtkEditable *editable, + gchar *new_text, + gint new_text_length, + gint *position) +{ + GtkTextAccessible *accessible; + gint length; + + if (new_text_length == 0) + return; + + accessible = GTK_TEXT_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable))); + length = g_utf8_strlen (new_text, new_text_length); + + g_signal_emit_by_name (accessible, + "text-changed::insert", + *position - length, + length); +} + +/* We connect to GtkEditable::delete-text, since it carries + * the information we need. But we delay emitting our own + * text_changed::delete signal until the entry has update + * all its internal state and emits GtkText::changed. + */ +static void +delete_text_cb (GtkEditable *editable, + gint start, + gint end) +{ + GtkTextAccessible *accessible; + + accessible = GTK_TEXT_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable))); + + if (end < 0) + { + gchar *text; + + text = gtk_text_get_display_text (GTK_TEXT (editable), 0, -1); + end = g_utf8_strlen (text, -1); + g_free (text); + } + + if (end == start) + return; + + g_signal_emit_by_name (accessible, + "text-changed::delete", + start, + end - start); +} + +static gboolean +check_for_selection_change (GtkTextAccessible *accessible, + GtkText *entry) +{ + gboolean ret_val = FALSE; + gint start, end; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) + { + if (end != accessible->priv->cursor_position || + start != accessible->priv->selection_bound) + /* + * This check is here as this function can be called + * for notification of selection_bound and current_pos. + * The values of current_pos and selection_bound may be the same + * for both notifications and we only want to generate one + * text_selection_changed signal. + */ + ret_val = TRUE; + } + else + { + /* We had a selection */ + ret_val = (accessible->priv->cursor_position != accessible->priv->selection_bound); + } + + accessible->priv->cursor_position = end; + accessible->priv->selection_bound = start; + + return ret_val; +} + +static gboolean +gtk_text_accessible_do_action (AtkAction *action, + gint i) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action)); + if (widget == NULL) + return FALSE; + + if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget)) + return FALSE; + + if (i != 0) + return FALSE; + + gtk_widget_activate (widget); + + return TRUE; +} + +static gint +gtk_text_accessible_get_n_actions (AtkAction *action) +{ + return 1; +} + +static const gchar * +gtk_text_accessible_get_keybinding (AtkAction *action, + gint i) +{ + GtkWidget *widget; + GtkWidget *label; + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + guint key_val; + + if (i != 0) + return NULL; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action)); + if (widget == NULL) + return NULL; + + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + if (!set) + return NULL; + + label = NULL; + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + + target_object = g_ptr_array_index (target, 0); + label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object)); + } + + g_object_unref (set); + + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_KEY_VoidSymbol) + return gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + + return NULL; +} + +static const gchar* +gtk_text_accessible_action_get_name (AtkAction *action, + gint i) +{ + if (i == 0) + return "activate"; + return NULL; +} + +static const gchar* +gtk_text_accessible_action_get_localized_name (AtkAction *action, + gint i) +{ + if (i == 0) + return C_("Action name", "Activate"); + return NULL; +} + +static const gchar* +gtk_text_accessible_action_get_description (AtkAction *action, + gint i) +{ + if (i == 0) + return C_("Action description", "Activates the entry"); + return NULL; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + iface->do_action = gtk_text_accessible_do_action; + iface->get_n_actions = gtk_text_accessible_get_n_actions; + iface->get_keybinding = gtk_text_accessible_get_keybinding; + iface->get_name = gtk_text_accessible_action_get_name; + iface->get_localized_name = gtk_text_accessible_action_get_localized_name; + iface->get_description = gtk_text_accessible_action_get_description; +} diff --git a/gtk/a11y/gtktextaccessible.h b/gtk/a11y/gtktextaccessible.h new file mode 100644 index 0000000000..e47be4236d --- /dev/null +++ b/gtk/a11y/gtktextaccessible.h @@ -0,0 +1,57 @@ +/* GTK+ - accessibility implementations + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TEXT_ACCESSIBLE_H__ +#define __GTK_TEXT_ACCESSIBLE_H__ + +#if !defined (__GTK_A11Y_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_TEXT_ACCESSIBLE (gtk_text_accessible_get_type ()) +#define GTK_TEXT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TEXT_ACCESSIBLE, GtkTextAccessible)) +#define GTK_TEXT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_ACCESSIBLE, GtkTextAccessibleClass)) +#define GTK_IS_TEXT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TEXT_ACCESSIBLE)) +#define GTK_IS_TEXT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_ACCESSIBLE)) +#define GTK_TEXT_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_ACCESSIBLE, GtkTextAccessibleClass)) + +typedef struct _GtkTextAccessible GtkTextAccessible; +typedef struct _GtkTextAccessibleClass GtkTextAccessibleClass; +typedef struct _GtkTextAccessiblePrivate GtkTextAccessiblePrivate; + +struct _GtkTextAccessible +{ + GtkWidgetAccessible parent; + + GtkTextAccessiblePrivate *priv; +}; + +struct _GtkTextAccessibleClass +{ + GtkWidgetAccessibleClass parent_class; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_text_accessible_get_type (void); + +G_END_DECLS + +#endif /* __GTK_TEXT_ACCESSIBLE_H__ */ diff --git a/gtk/a11y/meson.build b/gtk/a11y/meson.build index e1a1f86be9..4ea944d0fd 100644 --- a/gtk/a11y/meson.build +++ b/gtk/a11y/meson.build @@ -46,6 +46,7 @@ a11y_sources = files([ 'gtkstackaccessible.c', 'gtkstatusbaraccessible.c', 'gtkswitchaccessible.c', + 'gtktextaccessible.c', 'gtktextcellaccessible.c', 'gtktextviewaccessible.c', 'gtktogglebuttonaccessible.c', @@ -100,6 +101,7 @@ a11y_headers = files([ 'gtkstackaccessible.h', 'gtkstatusbaraccessible.h', 'gtkswitchaccessible.h', + 'gtktextaccessible.h', 'gtktextcellaccessible.h', 'gtktextviewaccessible.h', 'gtktogglebuttonaccessible.h', diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 8409d69e4c..0d54525daa 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -67,7 +67,7 @@ #include "gtkwidgetprivate.h" #include "gtkwindow.h" -#include "a11y/gtkentryaccessible.h" +#include "a11y/gtktextaccessible.h" #include #include @@ -228,17 +228,6 @@ struct _GtkTextPrivate guint populate_all : 1; }; -struct _EntryIconInfo -{ - GtkWidget *widget; - gchar *tooltip; - guint nonactivatable : 1; - guint in_drag : 1; - - GdkDragAction actions; - GdkContentFormats *target_list; -}; - struct _GtkTextPasswordHint { int position; /* Position (in text) of the last password hint */ @@ -1370,7 +1359,7 @@ gtk_text_class_init (GtkTextClass *class) gtk_binding_entry_add_signal (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK, "insert-emoji", 0); - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE); + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("text")); } -- 2.30.2